package gbench.sandbox.plot.geom;

import java.util.stream.*;
import java.text.NumberFormat;

import gbench.common.matlib.MatlibCanvas;
import gbench.common.matlib.MatlibCanvas.Range;
import gbench.common.matlib.data.Pipe;
import gbench.common.matlib.img.ImageWrapper;
import gbench.common.tree.LittleTree.IRecord;

import static java.lang.Math.PI;
import static java.lang.Math.exp;
import static java.lang.Math.sqrt;
import static gbench.common.matlib.MatlibCanvas.Range.rg;
import static gbench.common.matlib.MatlibCanvas.Range.rG;
import static gbench.common.matlib.img.ImageOps.square;
import static gbench.common.tree.LittleTree.IRecord.REC;

/**
 * 绘制概率分布
 * @author gbench
 *
 */
public class JunitPdf {
    
    /**
     * 绘制正态分布概率密度曲线
     */
    public static void norm_pdf() {
        final var size = 1000; // 绘图尺寸
        final var matlib = new MatlibCanvas(size); // 绘图对象
        final var scalex = 20; // 水平方向伸缩
        final var scaley = 500; //  垂直方向伸缩
        final var offsetx = 250; // 水平方向偏移
        final var offsety = 100; // 垂直方向偏移
        final var dnorm = Pipe.DUMMY(IRecord.class).bind(rec -> {
            final var mu = rec.dbl("mu"); // 均值
            final var sigma = rec.dbl("sigma"); // 方差
            // 根据 chebyshev定理，分布的数据主要 位于均值的若干方差单之内，所以 定义一个由均值+方差的区间范围
            return Range.NP2(sigma, 500).map(e -> 6 * e + mu) // 定义一个6sigma范围
               .map(matlib.fx(x -> 1 / (sigma * sqrt(2 * PI)) * exp(-1 / 2d * square((x - mu) / sigma))))
               .map(e -> e.affine(scalex, scaley, offsetx, offsety)) // 仿射变换数据 以 使得图形好看一点
               .flatMap(MatlibCanvas.nvec_continuous_line2()); // 数据连续化
        }); // dnorm
        
        matlib.plot(size, size / 2, pen -> {
            pen.setColor("#00ff00").drawPoints(dnorm.eval(REC("mu", 0, "sigma", 0.9)));
            pen.setColor("#ff0000").drawPoints(dnorm.eval(REC("mu", 0, "sigma", 1)));
            pen.setColor("#ffff00").drawPoints(dnorm.eval(REC("mu", 5, "sigma", 2)));
            pen.setColor("#00cccc").drawPoints(dnorm.eval(REC("mu", 10, "sigma", 3)));
        },null,true,img -> { // 绘制坐标轴
            final var nf = NumberFormat.getInstance(); // 数字格式化器
            final var g = new ImageWrapper(img, true) // 绘图器
                .drawHorizontalLine(rg(size), offsety - 4, "#ddccff") // 水平坐标轴
                .drawVerticalLine(rg(size / 2), offsetx, "#ddccff"); // 垂直坐标轴
            
            // 水平轴[-10,20]
            Range.P2(-10, 20, 10).reverseFlatMap(rng -> Stream.concat(
                rng.dblStream().map(x -> REC("color", "#888888", "x", x)), // 基准点数组
                Stream.of(0d, 5d, 10d).map(x -> REC("color", "#ffffff", "x", x)) // 强调色数据
            )).forEach(p -> g.drawString(nf.format(p.dbl("x")),
                 p.dbl("x") * scalex + offsetx, offsety-18, p.str("color"))); // 变换数值到绘图空间，绘制坐标刻度
            // 垂直轴:[-0.3,0.7]
            Range.P2(-0.3, 0.7, 10).reverseFlatMap(rng -> Stream.concat( 
                rng.dblStream().map(x -> REC("color", "#888888", "x", x)), // 基准点数组
                rG(-0.1,0.5,0.1).map(x -> REC("color", "#ffffff", "x", x)) // 强调色数据
            )).forEach(p -> g.drawString(nf.format(p.dbl("x")),
                 offsetx-40, scaley*p.dbl("x")+offsety, p.str("color"))); // 变换数值到绘图空间，绘制坐标刻度
        }); // plot
    }
    
    /**
     * 绘制正态分布曲线
     * @param args
     */
    public static void main(String args[]) {
        norm_pdf();
    }
}
